package com.warrior.superdata.mysql.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.warrior.superdata.base.BasePageResultVo;
import com.warrior.superdata.cache.ApiInfoCacheMap;
import com.warrior.superdata.constants.CommonConstant;
import com.warrior.superdata.constants.ResultCode;
import com.warrior.superdata.enums.ConditionType;
import com.warrior.superdata.enums.SortType;
import com.warrior.superdata.handler.MappingHandler;
import com.warrior.superdata.mysql.entity.*;
import com.warrior.superdata.mysql.entity.dto.*;
import com.warrior.superdata.mysql.mapper.ApiInfoMapper;
import com.warrior.superdata.mysql.service.*;
import com.warrior.superdata.util.PropertiesUtil;
import com.warrior.superdata.exceptionhandler.ApplicationException;
import com.warrior.superdata.util.ApiUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @Description:
 * @ClassName: com.warrior.wisdom.machine.room.mysql.service.impl.ApiInfoServiceImpl.java
 * @date: 2022/2/23 10:24
 * @author xujian
 */
@Service
@Slf4j
public class ApiInfoServiceImpl extends ServiceImpl<ApiInfoMapper, ApiInfo> implements ApiInfoService {

    @Autowired
    private ApiParamService apiParamService;

    @Autowired
    private ApiResultService apiResultService;

    @Autowired
    private ApiDatasourceSerivce apiDatasourceSerivce;

    @Autowired
    private ApiTableRelationService apiTableRelationService;

    @Autowired
    private ApiCustomizeSqlService apiCustomizeSqlService;

    @Autowired
    private PropertiesUtil propertiesUtil;

    @Autowired
    private MappingHandler mappingHandler;

    @Override
    public List<ApiInfoDetailDto> getApiInfoList(Integer id) {
        return this.getBaseMapper().getApiInfoList(id);
    }

    private void before(ApiInfoDetailDto apiInfoDetailDto) {
        ApiDatasource apiDatasource = apiDatasourceSerivce.
                checkApiDatasource(apiInfoDetailDto.getDatasourceCode());
        ApiDatasourceDto apiDatasourceDto = new ApiDatasourceDto();
        BeanUtils.copyProperties(apiDatasource, apiDatasourceDto);
        apiInfoDetailDto.setDatasourceDto(apiDatasourceDto);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void addApiInfo(ApiInfoDetailDto apiInfoDetailDto) {
        before(apiInfoDetailDto);
        if (CommonConstant.API_CUSTOMIZESQL.equals(apiInfoDetailDto.getIsCustomizeSql())) {
            parseCustomizeSql(apiInfoDetailDto);
        }

        ApiInfo apiInfo = new ApiInfo();
        BeanUtils.copyProperties(apiInfoDetailDto, apiInfo);

        this.save(apiInfo);

        if (CommonConstant.API_CUSTOMIZESQL.equals(apiInfoDetailDto.getIsCustomizeSql())) {
            ApiCustomizeSql apiCustomizeSql = new ApiCustomizeSql();
            BeanUtils.copyProperties(apiInfoDetailDto.getApiCustomizeSqlDto(), apiCustomizeSql);
            apiCustomizeSql.setApiId(apiInfo.getId());
            apiCustomizeSqlService.save(apiCustomizeSql);
        }

        List<ApiParam> apiParams = new ArrayList<>();
        apiInfoDetailDto.getParams().forEach(param -> {
            if (StringUtils.isNotBlank(param.getParamTableFieldType())) {
                String javaType = propertiesUtil.getJavaTypeByFieldType(param.getParamTableFieldType());
                param.setParamJavaType(javaType);
            }
            ApiParam apiParam = new ApiParam();
            BeanUtils.copyProperties(param, apiParam);
            apiParam.setApiId(apiInfo.getId());

            apiParams.add(apiParam);
        });
        apiParamService.addList(apiInfo.getId(), apiParams);

        List<ApiResult> apiResults = new ArrayList<>();
        apiInfoDetailDto.getResults().forEach(result -> {
            if (StringUtils.isNotBlank(result.getResultTableFieldType())) {
                String javaType = propertiesUtil.getJavaTypeByFieldType(result.getResultTableFieldType());
                result.setResultJavaType(javaType);
            }
            ApiResult apiResult = new ApiResult();
            BeanUtils.copyProperties(result, apiResult);
            apiResult.setApiId(apiInfo.getId());
            apiResults.add(apiResult);
        });
        apiResultService.addList(apiInfo.getId(), apiResults);

        List<ApiTableRelation> apiTableRelations = new ArrayList<>();
        if (!CollectionUtils.isEmpty(apiInfoDetailDto.getTableRelations())) {
            apiInfoDetailDto.getTableRelations().forEach(item -> {
                ApiTableRelation apiTableRelation = new ApiTableRelation();
                BeanUtils.copyProperties(item, apiTableRelation);
                apiTableRelation.setApiId(apiInfo.getId());
                apiTableRelations.add(apiTableRelation);
            });
        }
        apiTableRelationService.addList(apiTableRelations);

        mappingHandler.handlerApi(apiInfoDetailDto);
    }

    @Override
    public void updateApiInfo(ApiInfoDetailDto apiInfoDetailDto) {
        if (null == apiInfoDetailDto.getId()) {
            throw new ApplicationException(ResultCode.FAIL_CODE, "id不能为空");
        }
        mappingHandler.handlerRemoveApi(apiInfoDetailDto);

        before(apiInfoDetailDto);
        ApiInfo apiInfo = new ApiInfo();
        BeanUtils.copyProperties(apiInfoDetailDto, apiInfo);
        this.updateById(apiInfo);

        mappingHandler.handlerApi(apiInfoDetailDto);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void delete(Integer id) {
        if (null == id) {
            throw new ApplicationException(ResultCode.FAIL_CODE, "id不能为空");
        }

        ApiInfo apiInfo = this.getById(id);
        if (null == apiInfo) {
            throw new ApplicationException(ResultCode.FAIL_CODE, "该接口已被删除");
        }

        ApiInfoDetailDto apiInfoDetailDto = new ApiInfoDetailDto();
        apiInfoDetailDto.setUrl(apiInfo.getUrl());
        apiInfoDetailDto.setMethod(apiInfo.getMethod());
        ApiInfoDetailDto cache = ApiInfoCacheMap.API_INFO_MAP.get(ApiUtil.getApiUrl(apiInfoDetailDto));
        if (null != cache) {
            mappingHandler.handlerRemoveApi(cache);
        }

        this.removeById(id);
        apiParamService.deleteByApiId(id);
        apiResultService.deleteByApiId(id);
        apiTableRelationService.deleteByApiId(id);
    }

    @Override
    public void addApiParam(AddApiParamDto addApiParamDto) {
        if (!CollectionUtils.isEmpty(addApiParamDto.getList())) {
            List<ApiParam> saveList = new ArrayList<>();
            addApiParamDto.getList().forEach(item -> {
                ApiParam apiParam = new ApiParam();
                BeanUtils.copyProperties(item, apiParam);
                apiParam.setApiId(addApiParamDto.getApiId());
                saveList.add(apiParam);
            });

            apiParamService.saveBatch(saveList);
            refreshApi(addApiParamDto.getApiId());
        }
    }

    @Override
    public void deleteApiParam(AddApiParamDto addApiParamDto) {
        if (!CollectionUtils.isEmpty(addApiParamDto.getList())) {
            apiParamService.removeByIds(addApiParamDto.getList().stream().map
                    (ApiParamDto::getId).collect(Collectors.toList()));
            refreshApi(addApiParamDto.getApiId());
        }
    }

    @Override
    public void addApiResult(AddApiResultDto addApiResultDto) {
        if (!CollectionUtils.isEmpty(addApiResultDto.getList())) {
            List<ApiResult> saveList = new ArrayList<>();
            addApiResultDto.getList().forEach(item -> {
                ApiResult apiResult = new ApiResult();
                BeanUtils.copyProperties(item, apiResult);
                apiResult.setApiId(addApiResultDto.getApiId());
                saveList.add(apiResult);
            });
            apiResultService.saveBatch(saveList);

            refreshApiMap(addApiResultDto.getApiId());
        }
    }

    @Override
    public void deleteApiResutl(AddApiResultDto addApiResultDto) {
        if (!CollectionUtils.isEmpty(addApiResultDto.getList())) {
            apiResultService.removeByIds(addApiResultDto.getList().stream().map
                    (ApiResultDto::getId).collect(Collectors.toList()));
            refreshApiMap(addApiResultDto.getApiId());
        }
    }

    @Override
    public BasePageResultVo<List<ApiInfoDetailDto>> getApiInfoPage(ApiInfoDto apiInfoDto) {
        if (apiInfoDto.getPage() <= 0 || apiInfoDto.getSize() <= 0) {
            throw new ApplicationException(ResultCode.FAIL_CODE, "page和size不能为空");
        }
        BasePageResultVo result = new BasePageResultVo();
        Page<Object> page = PageHelper.startPage(apiInfoDto.getPage(), apiInfoDto.getSize());
        List<ApiInfoDetailDto> apiInfoByParam = this.getBaseMapper().getApiInfoByParam(apiInfoDto);
        if (!CollectionUtils.isEmpty(apiInfoByParam)) {
            apiInfoByParam.stream().forEach(item -> {
                item.setDatasourceType(item.getDatasourceDto().getType());
            });
        }
        result.setContent(apiInfoByParam);
        result.setPage(apiInfoDto.getPage());
        result.setSize(apiInfoDto.getSize());
        result.setTotal(page.getTotal());
        return result;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void saveOrUpdateApiInfo(ApiInfoDetailDto apiInfoDetailDto) {
        if (null == apiInfoDetailDto.getId()) {
            addApiInfo(apiInfoDetailDto);
        } else {
            delete(apiInfoDetailDto.getId());
            addApiInfo(apiInfoDetailDto);
        }
    }

    private String formatAllAssociation(String sql) {
        sql = handlerEveryAssociation(sql, "LEFT");
        sql = handlerEveryAssociation(sql, "RIGHT");
        sql = handlerEveryAssociation(sql, "INNER");
        return sql;
    }

    private String handlerEveryAssociation(String sql, String association) {
        int leftIndex = sql.indexOf(" " + association + " ");
        if (leftIndex != -1) {
            char[] chars = sql.toCharArray();
            int leftEndIndex = leftIndex + association.length() + 1;
            for (int i = leftEndIndex; leftEndIndex < chars.length; leftEndIndex++) {
                if (chars[leftEndIndex] != ' ') {
                    break;
                }
            }
            sql = sql.substring(0, leftIndex + association.length() + 2) + sql.substring(leftEndIndex, sql.length());
        }
        return sql;
    }

    /***
     * @description: 解析自定义sql
     * @param: apiInfo
     * @return: void
     * @author xujian
     * @date: 2022/8/31 18:28
     */
    public void parseCustomizeSql(ApiInfoDetailDto apiInfo) {
        if (apiInfo.getApiCustomizeSqlDto() == null ||
                StringUtils.isBlank(apiInfo.getApiCustomizeSqlDto().getCustomizeSql())) {
            throw new ApplicationException(ResultCode.FAIL_CODE, "自定义sql不能为空");
        }
        String sql = apiInfo.getApiCustomizeSqlDto().
                getCustomizeSql().replaceAll("\n", " ").replaceAll("\t", " ");
        String upCaseSql = sql.toUpperCase();
        //去除left join 等中间的空格（只保留一个空格，好为后面切割准备）
       // upCaseSql = formatAllAssociation(upCaseSql);

        ApiCustomizeSqlDto apiCustomizeSqlDto = new ApiCustomizeSqlDto();
        apiCustomizeSqlDto.setCustomizeSql(apiInfo.getApiCustomizeSqlDto().getCustomizeSql());
        apiInfo.setApiCustomizeSqlDto(apiCustomizeSqlDto);

        //是否有where条件
        int whereIndex = upCaseSql.toUpperCase().indexOf(" WHERE ");
        //是否有order 排序
        int orderIndex = upCaseSql.toUpperCase().indexOf(" ORDER ");

        String mainTable = null;
        if(upCaseSql.contains(" LEFT ") || upCaseSql.contains(" RIGHT ") || upCaseSql.contains(" INNER ")){
            int mainTableStartIndex = 6 + upCaseSql.indexOf(" FROM ");
            int mainTableEndIndex = 0;
            String tempMainTable = null;
            List<String> values = Arrays.asList(new String[]{" LEFT ", " RIGHT ", " INNER "});
            for (String value : values) {
                if (upCaseSql.indexOf(value) != -1) {
                    mainTableEndIndex = upCaseSql.indexOf(value);
                    tempMainTable = upCaseSql.substring(mainTableStartIndex, mainTableEndIndex);
                    if (!tempMainTable.contains(" LEFT ")
                            && !tempMainTable.contains(" RIGHT ")
                            && !tempMainTable.contains(" INNER ")) {
                        break;
                    }
                }
            }
            mainTable = sql.substring(mainTableStartIndex, mainTableEndIndex);

            String association = null;
            int mainIndex = upCaseSql.indexOf(tempMainTable) + tempMainTable.length();
            if (whereIndex != -1) {
                association = sql.substring(mainIndex, whereIndex);
            } else if (orderIndex != -1) {
                association = sql.substring(mainIndex, orderIndex);
            }

            log.info("association :{}", association);
            apiCustomizeSqlDto.setAssociationSql(association);
        }else {
            int mainTableStartIndex = 5 + upCaseSql.indexOf(" FROM ");
            int mainTableEndIndex = 0;
            if (whereIndex != -1) {
                mainTableEndIndex = upCaseSql.indexOf(" WHERE ");
            } else if (orderIndex != -1) {
                mainTableEndIndex = upCaseSql.indexOf(" ORDER ");
            }

            mainTable = sql.substring(mainTableStartIndex, mainTableEndIndex);
        }

        log.info("mainTable :{}", mainTable);
        apiInfo.setMainTableName(mainTable);

        Set<String> descSort = new HashSet<>();
        Set<String> ascSort = new HashSet<>();
        if (orderIndex != -1) {
            String sort = sql.substring(orderIndex + 6).replaceAll("BY ","").
                    replaceAll("By ","").
                    replaceAll("bY ","").
                    replaceAll("by ","");

            String[] sortFields = sort.split(",");
            for (String sortField : sortFields) {
                String[] fieldItem = sortField.trim().split(" ");
                if (sortField.toUpperCase().contains(" DESC")) {
                    descSort.add(fieldItem[0].replaceAll(" ", ""));
                }
                if (sortField.toUpperCase().contains(" ASC")) {
                    ascSort.add(fieldItem[0].replaceAll(" ", ""));
                }
            }
        }

        String resultSql = sql.substring(upCaseSql.indexOf("SELECT") + 7, upCaseSql.indexOf(" FROM "));
        String[] resultFields = resultSql.split(",");
        if (null == resultFields || resultFields.length == 0) {
            throw new ApplicationException(ResultCode.FAIL_CODE, "自定义sql没有查询字段");
        }

        List<ApiResultDto> apiResults = new ArrayList<>();
        for (String field : resultFields) {
            ApiResultDto apiResult = new ApiResultDto();
            String[] fieldSplit = field.split(" ");
            Boolean isFirst = Boolean.TRUE;
            for (String fieldName : fieldSplit) {
                if (StringUtils.isNotBlank(fieldName)) {
                    if (isFirst) {
                        apiResult.setResultTableFieldName(fieldName.replaceAll(" ", ""));
                        isFirst = Boolean.FALSE;
                    } else {
                        apiResult.setResultName(fieldName.replaceAll(" ", ""));
                        apiResult.setExportName(fieldName.replaceAll(" ", ""));
                    }
                    apiResult.setResultTableFieldType("varchar(64)");
                    apiResult.setResultJavaType("string");
                    apiResult.setSortType(SortType.NONE.getType());
                }
            }
            apiResults.add(apiResult);
        }

        apiInfo.setResults(apiResults);

        int paramStartIndex = 6;
        int paramEndIndex = 0;

        if (whereIndex != -1) {
            paramStartIndex = paramStartIndex + upCaseSql.indexOf(" WHERE ");
            if (orderIndex != -1) {
                paramEndIndex = upCaseSql.indexOf(" ORDER ");
            } else {
                paramEndIndex = upCaseSql.length();
            }

            List<ApiParamDto> apiParams = new ArrayList<>();
            String paramSql = sql.substring(paramStartIndex, paramEndIndex);
            String[] paramFields = null;

            paramSql = paramSql.replaceAll(" and ", " AND ");
            if (paramSql.contains(" AND ")) {
                paramFields = paramSql.split(" AND ");
            }

            for (String paramField : paramFields) {
                ApiParamDto apiParam = new ApiParamDto();
                String coditionName = null;
                for (ConditionType value : ConditionType.values()) {
                    if (paramField.contains(value.getName().replaceAll(" ", ""))) {
                        apiParam.setConditionType(value.getType());
                        coditionName = value.getName().replaceAll(" ", "");
                    }
                }

                String[] splitParam = paramField.split(coditionName);
                String paramName = splitParam[1].replaceAll(" ", "");
                if (paramName.contains(CommonConstant.BODY_PARAM_STR)) {
                    apiParam.setParamBodyType(CommonConstant.API_PARAM_BODY);
                    apiParam.setParamName(paramName.replaceAll(CommonConstant.BODY_PARAM_STR, ""));
                } else {
                    apiParam.setParamBodyType(CommonConstant.API_PARAM_URL);
                    apiParam.setParamName(paramName);
                }

                apiParam.setParamTableFieldName(splitParam[0].replaceAll(" ", ""));
                apiParam.setParamJavaType("string");
                apiParam.setParamTableFieldType("varchar(64)");
                apiParams.add(apiParam);
            }
            apiInfo.setParams(apiParams);
        }

        //排序字段处理
        for (String descFeild : descSort) {
            Boolean isExist = Boolean.FALSE;
            for (ApiResultDto apiResult : apiResults) {
                if (descFeild.equals(apiResult.getResultTableFieldType())) {
                    apiResult.setSortType(SortType.DESC.getType());
                    isExist = Boolean.TRUE;
                }
            }
            if (!isExist) {
                ApiResultDto apiResult = new ApiResultDto();
                apiResult.setSortType(SortType.DESC.getType());
                if(descFeild.contains(".")){
                    apiResult.setResultName(descFeild.split("\\.")[1]);
                }else{
                    apiResult.setResultName(descFeild);
                }
                apiResult.setResultTableFieldName(descFeild);
                apiResult.setResultTableFieldType("varchar(64)");
                apiResult.setResultJavaType("string");
                apiResults.add(apiResult);
            }
        }

        for (String ascFeild : ascSort) {
            Boolean isExist = Boolean.FALSE;
            for (ApiResultDto apiResult : apiResults) {
                if (ascFeild.equals(apiResult.getResultTableFieldType())) {
                    apiResult.setSortType(SortType.ASC.getType());
                    isExist = Boolean.TRUE;
                }
            }
            if (!isExist) {
                ApiResultDto apiResult = new ApiResultDto();
                apiResult.setSortType(SortType.ASC.getType());
                if(ascFeild.contains(".")){
                    apiResult.setResultName(ascFeild.split("\\.")[1]);
                }else{
                    apiResult.setResultName(ascFeild);
                }
                apiResult.setResultTableFieldName(ascFeild);
                apiResult.setResultTableFieldType("varchar(64)");
                apiResult.setResultJavaType("string");
                apiResults.add(apiResult);
            }
        }

    }

    private void refreshApi(Integer apiId) {
        List<ApiInfoDetailDto> apiInfoList = this.getBaseMapper().getApiInfoList(apiId);
        if (CollectionUtils.isEmpty(apiInfoList) || apiInfoList.size() > 1) {
            throw new ApplicationException(ResultCode.FAIL_CODE, "id有误");
        }

        ApiInfoDetailDto apiInfoDetailDto = apiInfoList.get(0);
        mappingHandler.handlerRemoveApi(apiInfoDetailDto);
        mappingHandler.handlerApi(apiInfoDetailDto);
    }

    private void refreshApiMap(Integer apiId) {
        List<ApiInfoDetailDto> apiInfoList = this.getBaseMapper().getApiInfoList(apiId);
        if (CollectionUtils.isEmpty(apiInfoList) || apiInfoList.size() > 1) {
            throw new ApplicationException(ResultCode.FAIL_CODE, "id有误");
        }

        ApiInfoDetailDto apiInfoDetailDto = apiInfoList.get(0);

        ApiInfoCacheMap.API_INFO_MAP.put(ApiUtil.getApiUrl(apiInfoDetailDto), apiInfoDetailDto);
    }


}
